﻿/// <reference path="custommanipulation.d.ts" />
import $ = require("jquery");
import ko = require("knockout");
import CallLimiter = require("../../utils/CallLimiter");

function clamp(num, min, max) {
    if (num < min) return min;
    if (max < num) return max;
    return num;
}

class CustomZoom {
    constructor($target: JQuery, callback: (value: number) => void) {
        this.target = <ManipElement>($target[0]);
        this.$target = $target;
        this.callback = CallLimiter(callback);
        this.target.ManipZoomInstance = this;
        this.target.ManipZoom = 1;
        this.bindEvents();
    }

    setZoom(value: number) {
        if (value == this.target.ManipZoom) {
            return;
        }
        this.zoomRel(value - this.target.ManipZoom);
    }

    bindEvents() {
        this.$target.on("manip.zoom", (event, args: IZoomChangedArgs) => {
            var origin = args.x;
            var scroll = this.target.ManipScrollInstance;
            if (scroll) {
                origin += this.target.ManipScrollX;
                //scroll.scrollRelative(args.relx, args.rely);
            }
            var delta = this.target.ManipZoom * (args.rel - 1);
            this.zoomRel(delta, origin);
        });

        this.target.addEventListener("wheel", (event) => {
            if (!event.ctrlKey) {
                return;
            }

            var dict = {};
            dict[event.DOM_DELTA_LINE] = 20;
            dict[event.DOM_DELTA_PAGE] = 600;
            dict[event.DOM_DELTA_PIXEL] = 1;
            var delta = this.target.ManipZoom * (-event.deltaY/600);
            this.zoomRel(delta, event.offsetX);
            event.stopPropagation();
        }, false);
    }

    zoomRel(deltaZoom: number, originX?: number, originY?: number) {
        var scroll = this.target.ManipScrollInstance;
        if (scroll) {
            var mulDelta = (this.target.ManipZoom + deltaZoom) / this.target.ManipZoom;

            originX = originX || this.target.ManipScrollX;
            originY = originY || this.target.ManipScrollY;

            var dx = (originX) * mulDelta - originX;
            var dy = (originY) * mulDelta - originY;
            scroll.scrollRelative(dx, dy);
        }
        this.target.ManipZoom = clamp(this.target.ManipZoom + deltaZoom, 0, Infinity);
        this.callback(this.target.ManipZoom);
    }

    target: ManipElement;
    $target: JQuery;
    callback: (value: number) => void;
}

class Zoom {
    public static create() {
        ko.bindingHandlers.CustomZoom = {
            init: (element, valueAccessor: () => KnockoutObservable<number>|any) => {
                var observable = valueAccessor();

                if (ko.isObservable(observable)) {
                    var customScroll = new CustomZoom($(element), (value: number) => {
                        observable(value);
                    });
                    customScroll.setZoom(observable());

                    ko.computed(() => {
                        customScroll.setZoom(ko.unwrap(<KnockoutObservable<number>|number> valueAccessor()));
                    });
                } else {
                    // ReSharper disable once WrongExpressionStatement
                    new CustomZoom($(element), (value: any) => { });
                }
            },
            update: () => { }
        }
    }
}

export = Zoom;